﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SharpSoft.WPF.WriteableBitmapModules
{
    /// <summary>
    /// 力学容器
    /// </summary>
    public class ForceWorld : AnimationModuleBase
    {
        public ForceWorld()
        {
            this.Field = new ForceField();
        }
        /// <summary>
        /// 力场
        /// </summary>
        public ForceField Field { get; }

        #region 初始信息

        /// <summary>
        /// 要进行动画的图像源
        /// </summary>
        public BitmapSource Source { get; set; }
        /// <summary>
        /// 源图像的初始位置
        /// </summary>
        public Point InitialPosition { get; set; }
        /// <summary>
        /// 分解图像源的网格信息
        /// </summary>
        public GridInfo GridInfo { get; set; } = new GridInfo(10);
        #endregion

        #region 力场

        /// <summary>
        /// 添加点力
        /// </summary>
        /// <param name="point"></param>
        /// <param name="value"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        public PointForce AddForce(Point point, double value, double radius = 0)
        {
            PointForce force = new PointForce() { Position = point, Value = value, Radius = radius };
            this.Field.Forces.Add(force);
            return force;
        }
        /// <summary>
        /// 添加平行力
        /// </summary>
        /// <param name="point"></param>
        /// <param name="direction"></param>
        /// <param name="value"></param>
        /// <param name="width"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        public ParallelForce AddForce(Point point, Vector direction, double value, double width = 0, double radius = 0)
        {
            ParallelForce force = new ParallelForce() { Position = point, Direction = direction, Value = value, Width = width, Radius = radius };
            this.Field.Forces.Add(force);
            return force;
        }
        /// <summary>
        /// 添加区域力
        /// </summary>
        /// <param name="area"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public AreaForce AddForce(Rect area, double value)
        {
            AreaForce force = new AreaForce() { Area = area, Value = value };
            this.Field.Forces.Add(force);
            return force;
        }
        /// <summary>
        /// 添加定位力
        /// </summary>
        /// <param name="value">力大小</param>
        /// <param name="globalPoint">源图像的全局定位</param>
        /// <returns></returns>
        public LocateForce AddForce(double value, Point globalPoint)
        {
            LocateForce force = new LocateForce() { Value = value };
            if (pieceWidth == 0 || pieceHeight == 0)
            {
                throw new Exception("当前碎片宽度或高度为0，是否尚未初始化？");
            }
            for (int r = 0; r < GridInfo.Rows; r++)
            {
                for (int c = 0; c < GridInfo.Columns; c++)
                {
                    var i = r * GridInfo.Columns + c;
                    if (i >= this.Field.Objects.Count)
                    {
                        throw new Exception("物理对象未初始化成功。");
                    }
                    var obj = this.Field.Objects[i];
                    var p = globalPoint + new Vector(pieceWidth * c, pieceHeight * r);
                    force.SetLocate(obj, p);
                }
            }

            this.Field.Forces.Add(force);
            return force;
        }
        #endregion

        #region 渲染

        private int pieceWidth, pieceHeight;//碎片的尺寸
        private WriteableBitmap[] pieces;

        /// <summary>
        /// 根据设置拆分源图像
        /// </summary>
        private void Split()
        {
            if (this.Source != null)
            {
                if (this.Source.PixelWidth < this.GridInfo.Columns || this.Source.PixelHeight < this.GridInfo.Rows)
                {
                    throw new Exception($"{Source.PixelWidth}x{Source.PixelHeight}的图像不足以分割成{GridInfo.Columns}x{GridInfo.Rows}块。");
                }
                var bmp = new WriteableBitmap(this.Source);
                pieceWidth = this.Source.PixelWidth / GridInfo.Columns;
                pieceHeight = this.Source.PixelHeight / GridInfo.Rows;
                var count = GridInfo.Rows * GridInfo.Columns;
                pieces = new WriteableBitmap[count];
                Rect souRect = new Rect(0, 0, pieceWidth, pieceHeight);
                Rect tarRect = new Rect(0, 0, pieceWidth, pieceHeight);

                for (int r = 0; r < GridInfo.Rows; r++)
                {
                    for (int c = 0; c < GridInfo.Columns; c++)
                    {
                        var i = r * GridInfo.Columns + c;
                        souRect.X = pieceWidth * c;
                        souRect.Y = pieceHeight * r;
                        var piece = new WriteableBitmap(pieceWidth, pieceHeight, bmp.DpiX, bmp.DpiY, bmp.Format, bmp.Palette);
                        piece.Lock();
                        piece.Blit(tarRect, bmp, souRect);
                        piece.Unlock();
                        pieces[i] = piece;
                    }
                }
            }
            else
            {
                throw new Exception("未指定动画的图像源。");
            }
        }
        public void Reset()
        {
            for (int r = 0; r < GridInfo.Rows; r++)
            {
                for (int c = 0; c < GridInfo.Columns; c++)
                {
                    var i = r * GridInfo.Columns + c;
                    var p = new Point(this.InitialPosition.X + pieceWidth * c, this.InitialPosition.Y + pieceHeight * r);
                    var obj = this.Field.Objects[i];
                    obj.Position = p;
                    obj.IsFixed = false;
                    obj.Velocity = new Vector(0, 0);
                    obj.RotationAngle = 0;
                    obj.AngularVelocity = 0;

                }
            }
            Field.Reset();
        }
        public override void Initialize()
        {
            this.Split();
            this.Field.Objects.Clear();
            this.Field.Forces.Clear();
            for (int r = 0; r < GridInfo.Rows; r++)
            {
                for (int c = 0; c < GridInfo.Columns; c++)
                {
                    var i = r * GridInfo.Columns + c;
                    var p = new Point(this.InitialPosition.X + pieceWidth * c, this.InitialPosition.Y + pieceHeight * r);
                    PhysicalObject po = new PhysicalObject() { Position = p };
                    this.Field.Objects.Add(po);
                }
            }
            base.Initialize();
        }

        public override void Start()
        {
            base.Start();
        }
        protected override void OnUpdate(AnimationTime time)
        {
            base.OnUpdate(time);
            Field.Update(time.TotalTime);
            bool allFixed = true;
            foreach (var item in Field.Objects)
            {
                if (!item.IsFixed)
                {
                    allFixed = false;
                    break;
                }
            }
            if (allFixed)
            {
                this.Stop();
                this.OnCompleted();
            }
        }
        protected override void OnRender(AnimationTime time)
        {
            var rt = this.RenderTarget;
            if (rt != null && pieces != null)
            {
                using (rt.GetBitmapContext())
                {
                    rt.Clear(Colors.Transparent);
                    Rect souRect = new Rect(0, 0, pieceWidth, pieceHeight);
                    Rect tarRect = new Rect(0, 0, pieceWidth, pieceHeight);
                    for (int r = 0; r < GridInfo.Rows; r++)
                    {
                        for (int c = 0; c < GridInfo.Columns; c++)
                        {
                            var i = r * GridInfo.Columns + c;
                            var piece = pieces[i];
                            var point = Field.Objects[i].Position;
                            tarRect.X = point.X;
                            tarRect.Y = point.Y;
                            rt.Blit(tarRect, piece, souRect);
                        }
                    }
                }
            }
            base.OnRender(time);
        }

        #endregion

    }

    /// <summary>
    /// 力场
    /// </summary>
    public class ForceField
    {
        public ForceField()
        {
            Objects = new List<PhysicalObject>();
            Forces = new ForceCollection();
        }
        /// <summary>
        /// 物体列表
        /// </summary>
        public List<PhysicalObject> Objects { get; }
        /// <summary>
        /// 力列表
        /// </summary>
        public ForceCollection Forces { get; }

        private TimeSpan lastTime;//上次更新的时间

        /// <summary>
        /// 重置当前力场的状态
        /// </summary>
        public void Reset()
        {
            lastTime = TimeSpan.Zero;
        }

        /// <summary>
        /// 更新力场中物体的状态
        /// </summary>
        /// <param name="time">过程的持续时间</param>
        public void Update(TimeSpan time)
        {
            var elapsed = time - lastTime;
            lastTime = time;

            //计算力场对物体的影响,并计算物体的新位置
            foreach (var obj in Objects)
            {
                if (obj.IsFixed)
                {//跳过固定的物体
                    continue;
                }
                foreach (var force in Forces)
                {
                    if (force.IsEnabled)
                    {
                        force.Apply(obj, elapsed);
                    }
                }

                if (!obj.IsFixed)
                {
                    obj.Position += obj.Velocity * elapsed.TotalSeconds;//计算物体的新位置
                    if (obj.AngularVelocity != 0)
                    {
                        obj.RotationAngle += obj.RotationAngle * elapsed.TotalSeconds;//计算物体的新角度
                    }
                }
            }
            //处理碰撞
            ProcessCollisions();
        }

        /// <summary>
        /// 处理物体间的碰撞
        /// </summary>
        private void ProcessCollisions()
        {
            List<CollisionPair> pairs = new List<CollisionPair>();
            foreach (var item in this.Objects)
            {
                foreach (var other in this.Objects)
                {
                    if (other != item)
                    {
                        CollisionPair cp = new CollisionPair() { Object1 = item, Object2 = other };
                        if (!pairs.Contains(cp))
                        {//不存在这一对
                            if (item.IsCollisionWith(other))
                            {
                                pairs.Add(cp);
                            }

                        }
                        else
                        {
                            continue;
                        }
                    }
                }
            }

            /*
             * 碰撞后动能分配 碰撞前速度位v1 v2 质量为m1 m2
             
（动量守恒）m1v1 + m2v2 = m1v1' + m2v2'
（动能守恒）m1v1²/2 + m2v2²/2 = m1v1'²/2 + m2v2'²/2
综合得到：
v1' = [(m1-m2)v1 + 2m2v2] / (m1+m2) 　　
v2' = [(m2-m1)v2 + 2m1v1] / (m1+m2)
             */
            //弹性碰撞
            foreach (var pair in pairs)
            {
                var o1 = pair.Object1;
                var o2 = pair.Object2;
                var v1 = o1.Velocity;
                var v2 = o2.Velocity;
                if (o1.IsFixed && !o2.IsFixed)
                {//o1固定
                    //TODO:应该计算碰撞切面
                    v2.Negate();
                    o2.Velocity = v2;
                }
                else if (!o1.IsFixed && o2.IsFixed)
                {//o2固定
                    //TODO:应该计算碰撞切面
                    v1.Negate();
                    o1.Velocity = v1;
                }
                else if (o1.IsFixed && o2.IsFixed)
                {//两个物体都固定则不处理
                    continue;
                }
                else
                {//都是运动的物体则按动量守恒公式计算
                    var m1 = o1.Mass;
                    var m2 = o2.Mass;
                    var v1_new = ((m1 - m2) * v1 + 2 * m2 * v2) / (m1 + m2);
                    var v2_new = ((m2 - m1) * v2 + 2 * m1 * v1) / (m2 + m1);
                    pair.Object1.Velocity = v1_new;
                    pair.Object2.Velocity = v2_new;
                }

            }
        }

    }

    /// <summary>
    /// 物理对象
    /// </summary>
    public class PhysicalObject
    {
        private static readonly Vector zeroVector = new Vector(0, 0);
        /// <summary>
        /// 质量
        /// </summary>
        public double Mass { get; set; } = 1;

        private Vector _Velocity;

        /// <summary>
        /// 运动速度
        /// </summary> 
        public Vector Velocity
        {
            get
            {
                if (this.IsFixed)
                {//已经固定的物体速度始终为0
                    return zeroVector;
                }
                return _Velocity;
            }
            set { _Velocity = value; }
        }

        /// <summary>
        /// 角速度
        /// </summary>
        public double AngularVelocity { get; set; }
        /// <summary>
        /// 当前的旋转角度
        /// </summary>
        public double RotationAngle { get; set; }
        /// <summary>
        /// 当前位置
        /// </summary> 
        public Point Position { get; set; }
        /// <summary>
        /// 碰撞盒子
        /// </summary>
        public CollisionBox CollisionBox { get; set; }
        /// <summary>
        /// 是否固定，已经固定的物体不再受力的影响。
        /// </summary>
        public bool IsFixed { get; set; } = false;

        /// <summary>
        /// 判断当前物体是否与另一个物体发了碰撞
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool IsCollisionWith(PhysicalObject other)
        {
            if (other == this)
            {//不要与自己检测碰撞
                return false;
            }
            if (other == null) return false;
            if (this.CollisionBox == null || other.CollisionBox == null)
            {
                return false;
            }
            return this.CollisionBox.IsCollisionWith(other.CollisionBox);

        }
    }
    /// <summary>
    /// 碰撞对(描述一对物体的碰撞)
    /// </summary>
    public class CollisionPair
    {
        public PhysicalObject Object1 { get; set; }
        public PhysicalObject Object2 { get; set; }

        public override bool Equals(object obj)
        {
            if (obj is CollisionPair cp)
            {
                return cp.Object1 == this.Object1 && cp.Object2 == this.Object2;
            }
            return false;
        }

        public static bool operator ==(CollisionPair cp1, CollisionPair cp2)
        {
            if (cp1 == null)
            {
                return cp2 == null;
            }
            else
            {
                return cp1.Equals(cp2);
            }
        }
        public static bool operator !=(CollisionPair cp1, CollisionPair cp2)
        {
            return !(cp1 == cp2);
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }

    /// <summary>
    /// 碰撞盒子
    /// </summary>
    public class CollisionBox
    {
        public CollisionBox(PhysicalObject physicalObject)
        {
            this.Object = physicalObject;
        }
        public PhysicalObject Object { get; }
        /// <summary>
        /// 物体用于检测碰撞的多个矩形框，这些矩形框的坐标系相对于物体本身的位置。
        /// </summary>
        public Rect[] Boxes { get; set; }

        /// <summary>
        /// 碰撞标记（位标记，具有相同位的两个物体即会发生碰撞）
        /// </summary> 
        public ulong CollisionFlag { get; set; } = ulong.MaxValue;
        /// <summary>
        /// 添加当前碰撞标记的指定位（碰撞标记为0-63，表示设置第1-64位）
        /// </summary>
        /// <param name="bit">0-63</param>
        public void AddCollisionFlag(byte bit)
        {
            double v = Math.Pow(2, bit);
#if NET5_0_OR_GREATER
            ulong uv = BitConverter.DoubleToUInt64Bits(v);
#else
            byte[] buf = BitConverter.GetBytes(v);
            ulong uv = BitConverter.ToUInt64(buf, 0);
#endif

            this.CollisionFlag |= uv;
        }

        /// <summary>
        /// 获取盒子的全局坐标
        /// </summary>
        /// <returns></returns>
        private Rect[] inWorld()
        {
            if (Boxes == null)
            {
                return null;
            }
            Rect[] outWorld = new Rect[Boxes.Length];
            for (int i = 0; i < Boxes.Length; i++)
            {
                var l = Boxes[i].Location + (Object.Position - new Point(0, 0));
                outWorld[i] = new Rect(l, Boxes[i].Size);
            }
            return outWorld;

        }
        /// <summary>
        /// 断言与另一个碰撞盒子是否发生碰撞
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool IsCollisionWith(CollisionBox other)
        {
            if (other == null || other.Boxes == null || other.Boxes.Length == 0)
            {
                return false;
            }
            if (this.Boxes == null || this.Boxes.Length == 0)
            {
                return false;
            }
            if ((CollisionFlag & other.CollisionFlag) == 0uL)
            {//碰撞标记没有重叠
                return false;
            }
            //判断当前的矩形是否与其它碰撞盒子的所有举行是否有相交
            foreach (var rect1 in this.Boxes)
            {
                foreach (var rect2 in other.Boxes)
                {
                    if (rect1.IntersectsWith(rect2))
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    /// <summary>
    /// 力列表
    /// </summary>
    public class ForceCollection : ICollection<IForce>
    {
        private readonly List<IForce> AreaForces;//无方向的力
        private readonly List<IForce> DirForces;//有方向的力

        public ForceCollection()
        {
            AreaForces = new List<IForce>();
            DirForces = new List<IForce>();
        }
        public int Count => AreaForces.Count + DirForces.Count;

        public IForce this[int index]
        {
            get
            {

                if (index >= 0 && index < this.Count)
                {
                    if (index < DirForces.Count)
                    {
                        return DirForces[index];
                    }
                    else
                    {
                        return AreaForces[index - DirForces.Count];
                    }
                }
                throw new ArgumentOutOfRangeException(nameof(index));
            }
        }

        public bool IsReadOnly => false;

        public void Add(IForce item)
        {
            if (item is AreaForce af)
            {
                AreaForces.Add(af);
            }
            else
            {
                DirForces.Add(item);
            }
        }

        public void Clear()
        {
            AreaForces.Clear();
            DirForces.Clear();
        }

        public bool Contains(IForce item)
        {
            return AreaForces.Contains(item) || DirForces.Contains(item);
        }

        public void CopyTo(IForce[] array, int arrayIndex)
        {
            DirForces.CopyTo(array, arrayIndex);
            AreaForces.CopyTo(array, arrayIndex + DirForces.Count);
        }

        public IEnumerator<IForce> GetEnumerator()
        {//迭代时区域力始终位于最后
            foreach (var item in DirForces)
            {
                yield return item;
            }
            foreach (var item in AreaForces)
            {
                yield return item;
            }
            yield break;
        }

        public bool Remove(IForce item)
        {
            if (AreaForces.Contains(item))
            {
                AreaForces.Remove(item);
            }
            else if (DirForces.Contains(item))
            {
                DirForces.Remove(item);
            }
            else
            {
                return false;
            }
            return true;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (var item in DirForces)
            {
                yield return item;
            }
            foreach (var item in AreaForces)
            {
                yield return item;
            }
            yield break;
        }
    }


    public interface IForce
    {
        //牛顿第二定律 F=ma

        double Value { get; set; }
        bool IsEnabled { get; set; }
        void Apply(PhysicalObject physicalObject, TimeSpan elapsed);
    }

    /// <summary>
    /// 点力，这个力源力的方向始终平行于力源位置和目标位置之间的线段。
    /// </summary>
    public class PointForce : IForce
    {
        public bool IsEnabled { get; set; } = true;
        /// <summary>
        /// 力源的位置
        /// </summary>
        public Point Position { get; set; }
        /// <summary>
        /// 力的大小（正数为向外推的力，负数为向内吸引的力）
        /// </summary>
        public double Value { get; set; }
        /// <summary>
        /// 力的作用半径,为0表示无穷大
        /// </summary>
        public double Radius { get; set; }
        /// <summary>
        /// 指定一个函数用于计算力的变化值,第一个参数为当前力的初始值，第二个参数为当前作用的物体，返回新的力大小。
        /// </summary>
        public Func<PointForce, PhysicalObject, double> DecayEffect { get; set; } = DefaultDecayEffect;

        /// <summary>
        /// 默认的力效果
        /// </summary>
        /// <param name="curForce"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static double DefaultDecayEffect(PointForce curForce, PhysicalObject obj)
        {
            if ((obj.Position - curForce.Position).Length > 500)
            {
                return -curForce.Value;
            }
            return curForce.Value;
        }

        public void Apply(PhysicalObject physicalObject, TimeSpan elapsed)
        {
            var dir = physicalObject.Position - this.Position;
            if (this.Radius <= 0 || dir.Length <= this.Radius)
            {//在作用范围内
                double f = this.Value;
                if (DecayEffect != null)
                {//计算力效果
                    f = DecayEffect(this, physicalObject);
                }
                var acc = f / physicalObject.Mass;//计算加速度
                var v = acc * elapsed.TotalSeconds;//速度变化量
                dir.Normalize();//将方向标准化
                Vector va = v * dir;//速度增量
                physicalObject.Velocity += va;
            }

        }
    }
    /// <summary>
    /// 平行力
    /// </summary>
    public class ParallelForce : IForce
    {
        public bool IsEnabled { get; set; } = true;
        /// <summary>
        /// 力源的位置
        /// </summary>
        public Point Position { get; set; }
        /// <summary>
        /// 平行力的方向
        /// </summary>
        public Vector Direction { get; set; }
        /// <summary>
        /// 力的大小
        /// </summary>
        public double Value { get; set; }
        /// <summary>
        /// 力源的宽度(为0表示无穷宽)
        /// </summary> 
        public double Width { get; set; }
        /// <summary>
        /// 力的作用半径,为0表示无穷大
        /// </summary>
        public double Radius { get; set; }

        /// <summary>
        /// 指定一个函数用于计算力的变化值,第一个参数为当前力的初始值，第二个参数为当前作用的物体，返回新的力大小。
        /// </summary>
        public Func<ParallelForce, PhysicalObject, double> DecayEffect { get; set; } = null;


        public void Apply(PhysicalObject physicalObject, TimeSpan elapsed)
        {
            var dir = physicalObject.Position - this.Position;
            var angle = Vector.AngleBetween(this.Direction, dir);
            if (angle <= 90 && angle >= -90)//TODO:使宽度生效
            {//在作用范围内
                double f = this.Value;
                if (DecayEffect != null)
                {//计算力效果
                    f = DecayEffect(this, physicalObject);
                }
                var acc = f / physicalObject.Mass;//计算加速度
                var v = acc * elapsed.TotalSeconds;//速度变化量
                var vec = this.Direction;
                vec.Normalize();//将方向标准化
                Vector va = v * vec;//速度增量
                physicalObject.Velocity += va;
            }
        }
    }

    /// <summary>
    /// 定位力，这个力会迫使物体始终向指定的坐标点运动，直到达到这个坐标点则静止（一旦到达目标点，物体将不再受其他力影响）
    /// </summary>
    public class LocateForce : IForce
    {

        public bool IsEnabled { get; set; } = true;

        private readonly Dictionary<PhysicalObject, Point> poses = new Dictionary<PhysicalObject, Point>();

        public Point this[PhysicalObject obj]
        {
            get { return poses[obj]; }
            set { poses[obj] = value; }
        }
        /// <summary>
        /// 设置对象的定位点
        /// </summary>
        /// <param name="physicalObject"></param>
        /// <param name="point"></param>
        public void SetLocate(PhysicalObject physicalObject, Point point)
        {
            poses[physicalObject] = point;
        }
        /// <summary>
        /// 力的大小
        /// </summary>
        public double Value { get; set; }
        /// <summary>
        /// 是否是绝对的，如果为true则将会直接拔除其他方向的位移
        /// </summary>
        public bool IsAbsolute { get; set; } = true;

        /// <summary>
        /// 指定一个函数用于计算力的变化值,第一个参数为当前力的初始值，第二个参数为当前作用的物体，返回新的力大小。
        /// </summary>
        public Func<LocateForce, PhysicalObject, double> DecayEffect { get; set; } = null;

        public void Clear()
        {
            poses.Clear();
        }

        public void Apply(PhysicalObject physicalObject, TimeSpan elapsed)
        {
            if (poses.ContainsKey(physicalObject))
            {//该物体受影响
                var p = poses[physicalObject];//目标点
                var dir = p - physicalObject.Position;//吸引方向
                double f = this.Value;
                if (DecayEffect != null)
                {//计算力的衰减
                    f = DecayEffect(this, physicalObject);
                }

                if (this.IsAbsolute)
                {//绝对定位功能将其他方向的运动速度约束在定位力方向上
                    physicalObject.Velocity = VectorHelper.Projection(dir, physicalObject.Velocity);
                }

                var acc = f / physicalObject.Mass;//计算加速度
                var v = acc * elapsed.TotalSeconds;//速度变化量
                var unitV = dir;
                unitV.Normalize();//将方向标准化
                Vector va = v * unitV;//速度增量
                physicalObject.Velocity += va;
                var angle = Vector.AngleBetween(dir, physicalObject.Velocity);
                if (angle >= -10 && angle <= 10)
                {//当前物体当前运动方向与固定点方位的夹角不超过10度则判断是否将固定

                    if (physicalObject.Velocity.Length * elapsed.TotalSeconds >= dir.Length)
                    {//这一帧过后物体将会达到或超过目标点
                     //将物体固定在目标点
                        physicalObject.Position = p;
                        physicalObject.IsFixed = true;
                    }
                }

            }
        }
    }

    /// <summary>
    /// 区域力（在一定区域内影响物体运动的力，这个力没有作用方向，只会使物体趋向静止或运动更快，如阻力）。
    /// 如果是阻力则只能使物体静止而不能使其向反方向运动。
    /// </summary>
    public class AreaForce : IForce
    {
        public bool IsEnabled { get; set; } = true;
        public Rect Area { get; set; }
        /// <summary>
        /// 力的大小(为负会迫使物体减速，为正会驱使物体运动更快)
        /// </summary>
        public double Value { get; set; }

        /// <summary>
        /// 指定一个函数用于计算力的变化值,第一个参数为当前力的初始值，第二个参数为当前作用的物体，返回新的力大小。
        /// </summary>
        public Func<AreaForce, PhysicalObject, double> DecayEffect { get; set; } = null;

        public void Apply(PhysicalObject physicalObject, TimeSpan elapsed)
        {
            if (Area.Contains(physicalObject.Position))
            {//在作用范围内
                var vec = physicalObject.Velocity;
                if (vec.X == 0 && vec.Y == 0)
                {//区域力不对静止的物体产生作用
                    return;
                }
                double f = this.Value;
                if (DecayEffect != null)
                {
                    f = DecayEffect(this, physicalObject);
                }
                var acc = f / 1;//计算加速度,这个力产生的速度变化不在普适的受质量影响
                var vo = acc * elapsed.TotalSeconds;//速度值变化量
                vec.Normalize();
                var vc = (vec * vo);//速度向量的变化量
                if (vo > 0)
                {//加速度
                    physicalObject.Velocity += vc;
                }
                else if (vo < 0)
                {//减速度
                    if (vc.Length >= physicalObject.Velocity.Length)
                    {//足以使物体静止,直接设速度为0，避免反向运动
                        physicalObject.Velocity = new Vector(0d, 0d);
                    }
                    else
                    {
                        physicalObject.Velocity += vc;
                    }
                }
            }
        }
    }
}
